<?php
/**
 ***********************************************************************************************************************
 * Artisan
 ***********************************************************************************************************************
 */

namespace artisan;

use artisan\exception\handler;

/**
 * Class artisan
 */
class artisan
{
    /**
     * @var artisan 系统实例
     */
    public static $instance;

    /**
     * @var \artisan\route 系统路由
     */
    public static $route;

    /**
     * @var string 系统输出格式
     */
    public static $output_format = null;

    /**
     * @var array 系统容器
     */
    public $container;

    /**
     * @var array 系统容器
     */
    public static $containers;

    /**
     * @var array 兼容旧版核心类别名
     */
    private static $alias = array(
        'cache' => '\\artisan\\cache',
        'db' => '\\artisan\\db',
        'format' => '\\artisan\\format',
        'http' => '\\artisan\\http',
    );

    /**
     * 初始化一个Artisan
     */
    public function __construct()
    {
        // ① 注册自动加载类
        self::registerAutoloader();

        // ② 设置系统时区
        date_default_timezone_set(config::get('global', 'timezone') ?: 'Asia/Shanghai');

        // ③ 装载系统函数
        foreach([VENDORPATH . 'artisan/', APPPATH] as $path) {
            $file = $path . 'helper/helper.php';
            file_exists($file) && require $file;
        }

        // ④ 初始化异常处理
        $errorHandler = globalConfig('error.handler');
        $errorReporting = globalConfig('error.reporting');
        // 尝试寻找用户自定义异常处理
        !empty($errorHandler) ? new $errorHandler($errorReporting) : new handler($errorReporting);

        // ⑤ 定义请求结束后事件处理函数
        register_shutdown_function(__NAMESPACE__ . "\\output::finish");

        //临时设置socket超时,3分钟
        //ini_set('default_socket_timeout', 120);

        //设置默认最大执行时间:线上默认30秒太短
        //ini_set('max_execution_time', 120);
    }

    /**
     * PSR-0 自动载入类
     */
    public static function registerAutoloader()
    {
        spl_autoload_register(__NAMESPACE__ . "\\artisan::autoload");
    }


    /**
     * PSR-0 自动载入类
     *
     * @param $className
     * @return bool|mixed
     */
    public static function autoload($className)
    {
        // 机制：首选加载app_path下的类，找不到尝试加载vendor_path下的类
        $path_map = [APPPATH, preg_replace('/'.__NAMESPACE__.'\/$/', '', VENDORPATH)];

        foreach($path_map as $path) {
            $file = self::fetchClass($className, $path);
            if(file_exists($file) && !class_exists($className)) {
                return require $file;
            }
        }

        // 兼容：继续寻找类别名
        $arr = explode('\\', $className);
        $realClass = array_pop($arr);
        if(isset(self::$alias[$realClass])) {
            if(!class_exists(self::$alias[$realClass])) {
                $file = self::fetchClass(self::$alias[$realClass], VENDORPATH);
                if(file_exists($file)) {
                    require $file;
                    return class_alias(self::$alias[$realClass], $className);
                }
            } else {
                return class_alias(self::$alias[$realClass], $className);
            }
        }

        return true;
    }

    /**
     * 解析出类的真实文件地址
     *
     * @param $className
     * @param $path
     * @return string
     */
    public static function fetchClass($className, $path = '')
    {
        $baseDir = $path;
        $thisClass = str_replace(__NAMESPACE__.'\\', '', __CLASS__);
        if(substr($baseDir, -strlen($thisClass)) === $thisClass) {
            $baseDir = substr($baseDir, 0, -strlen($thisClass));
        }
        $className = ltrim($className, '\\');
        $fileName  = $baseDir;
        if ($lastNsPos = strripos($className, '\\')) {
            $namespace = substr($className, 0, $lastNsPos);
            $className = substr($className, $lastNsPos + 1);
            $fileName  .= str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
        }
        $fileName .= $className . '.php';
        return $fileName;
    }

    /**
     * 路由解析 注：cron脚步无须执行路由解析
     */
    public function run()
    {
        self::$route = new route();
        self::$route->dispatch();
    }

}